// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#include <stdio.h>

#include <turbo/log/logging.h>
#include <gtest/gtest.h>
#include <pollux/type/variant.h>
#include <pollux/vector/flat_vector.h>
#include <pollux/vector/simple_vector.h>
#include <pollux/vector/variant_to_vector.h>

using namespace kumo::pollux;

class VariantToVectorTest : public testing::Test {
  std::shared_ptr<memory::MemoryPool> pool_{
      memory::memoryManager()->addLeafPool()};

 public:
  static void SetUpTestCase() {
    memory::MemoryManager::testingSetInstance({});
  }

  template <TypeKind KIND>
  void testCreateVector(
      const TypePtr& type,
      const std::vector<variant>& inputArray) {
    using TCpp = typename TypeTraits<KIND>::NativeType;

    auto varArray = variant::array(inputArray);
    auto array_vector =
        core::variantArrayToVector(type, varArray.array(), pool_.get());
    ASSERT_TRUE(array_vector != nullptr);
    ASSERT_EQ(1, array_vector->size());

    auto elements = array_vector->elements()->as<FlatVector<TCpp>>();
    if (!inputArray.empty()) {
      ASSERT_TRUE(elements != nullptr);
    }

    for (size_t i = 0; i < inputArray.size(); i++) {
      auto& var = inputArray[i];
      if (var.isNull()) {
        ASSERT_TRUE(elements->is_null_at(i));
      } else if constexpr (std::is_same_v<TCpp, StringView>) {
        ASSERT_EQ(
            var.template value<KIND>(), std::string(elements->value_at(i)));
      } else {
        ASSERT_EQ(var.template value<KIND>(), elements->value_at(i));
      }
    }
  }
};

TEST_F(VariantToVectorTest, bigint) {
  testCreateVector<TypeKind::BIGINT>(
      ARRAY(BIGINT()),
      {
          variant::create<TypeKind::BIGINT>(4432),
          variant::null(TypeKind::BIGINT),
          variant::create<TypeKind::BIGINT>(-123456789),
      });
}

TEST_F(VariantToVectorTest, integer) {
  testCreateVector<TypeKind::INTEGER>(
      ARRAY(INTEGER()),
      {
          variant::create<TypeKind::INTEGER>(122133),
          variant::create<TypeKind::INTEGER>(35121),
      });
}

TEST_F(VariantToVectorTest, smallint) {
  testCreateVector<TypeKind::SMALLINT>(
      ARRAY(SMALLINT()),
      {
          variant::create<TypeKind::SMALLINT>(123),
          variant::create<TypeKind::SMALLINT>(-63),
          variant::null(TypeKind::SMALLINT),
      });
}

TEST_F(VariantToVectorTest, tinyint) {
  testCreateVector<TypeKind::TINYINT>(
      ARRAY(TINYINT()),
      {
          variant::create<TypeKind::TINYINT>(-12),
          variant::create<TypeKind::TINYINT>(51),
      });
}

TEST_F(VariantToVectorTest, boolean) {
  testCreateVector<TypeKind::BOOLEAN>(
      ARRAY(BOOLEAN()),
      {
          variant::null(TypeKind::BOOLEAN),
          variant::create<TypeKind::BOOLEAN>(false),
          variant::create<TypeKind::BOOLEAN>(true),
      });
}

TEST_F(VariantToVectorTest, real) {
  testCreateVector<TypeKind::REAL>(
      ARRAY(REAL()),
      {
          variant::create<TypeKind::REAL>(-4.78),
          variant::create<TypeKind::REAL>(123.45),
      });
}

TEST_F(VariantToVectorTest, double) {
  testCreateVector<TypeKind::DOUBLE>(
      ARRAY(DOUBLE()),
      {
          variant::create<TypeKind::DOUBLE>(-99.948),
          variant::create<TypeKind::DOUBLE>(-123.456),
          variant::create<TypeKind::DOUBLE>(78.91),
          variant::null(TypeKind::DOUBLE),
      });
}

TEST_F(VariantToVectorTest, varchar) {
  testCreateVector<TypeKind::VARCHAR>(
      ARRAY(VARCHAR()),
      {
          variant::create<TypeKind::VARCHAR>("hello"),
          variant::create<TypeKind::VARCHAR>("world"),
          variant::create<TypeKind::VARCHAR>(
              "Some longer string that doesn't get inlined..."),
      });
}

TEST_F(VariantToVectorTest, varbinary) {
  testCreateVector<TypeKind::VARBINARY>(
      ARRAY(VARBINARY()),
      {
          variant::create<TypeKind::VARBINARY>("hello"),
          variant::create<TypeKind::VARBINARY>("world"),
          variant::create<TypeKind::VARBINARY>(
              "Some longer string that doesn't get inlined..."),
      });
}

TEST_F(VariantToVectorTest, empty) {
  testCreateVector<TypeKind::BIGINT>(ARRAY(BIGINT()), {});
}
